import logging
import json

from django.shortcuts import render
from django.views import View
from django.http import HttpResponseNotFound, Http404
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page

from . import models
from . import constants
from python_study import settings
from utils.json_fun import to_json_data
from utils.res_code import Code, error_map

# 导入日志器
logger = logging.getLogger('django')


# Create your views here.

@method_decorator(cache_page(timeout=120, cache='page_cache'), name='dispatch')
class IndexView(View):
    """
    Create Index View
    url定义：''
    """

    def get(self, request):
        """
        渲染index页，将标签数据传出
        """
        tags = models.Tag.objects.only('id', 'name').filter(is_delete=False)

        hot_news = models.HotNews.objects.select_related('news').\
                       only('news__title', 'news__image_url', 'news__id').\
                       filter(is_delete=False).order_by('priority', '-news__clicks')[0:constants.SHOW_HOTNEWS_COUNT]

        # locals() 返回是当前局部变量的深拷贝，返回字典类型的局部变量
        return render(request, 'news/index.html', locals())


@method_decorator(cache_page(timeout=120, cache='page_cache'), name='dispatch')
class NewsListView(View):
    """
    Creat news lists view function.

    在xshell中导入测试数据，在xshell中通过rz命令，将tb_news_20181217.sql文件上传到虚拟机
    mysql -u 用户名 -p -D 数据库名 < tb_news_20181217.sql

    业务处理流程：

    - 判断前端传的标签分类id是否为空，是否为整数、是否超过范围
    - 判断前端传的当前文章页数是否为空，是否为整数、是否超过范围

    请求方法：GET

    url定义：/news/

    """

    def get(self, request):
        # 判断tag_id是否为空，是否可以转为int
        try:
            tag_id = int(request.GET.get('tag_id', 0))
        except Exception as e:
            logger.error('标签错误：\n{}'.format(e))
            tag_id = 0

        # 判断page是否为空，是否可以转为int
        try:
            page = int(request.GET.get('page', 1))
        except Exception as e:
            logger.error('当前页数错误：\n{}'.format(e))
            page = 1

        news_queryset = models.News.objects.select_related('tag', 'author'). \
            only('title', 'digest', 'image_url', 'update_time', 'tag__name', 'author__username')

        # tag_id字段
        news = news_queryset.filter(is_delete=False, tag_id=tag_id) or \
               news_queryset.filter(is_delete=False)

        paginator = Paginator(news, constants.PER_PAGE_NEWS_COUNT)
        try:
            news_info = paginator.page(page)
        except Exception as e:
            # 若用户访问的页数大于实际页数，则返回最后一页数据
            logger.info("用户访问的页数大于总页数。")
            news_info = paginator.page(paginator.num_pages)

        # 序列化输出
        news_info_list = []
        for n in news_info:
            news_info_list.append({
                'id': n.id,
                'title': n.title,
                'digest': n.digest,
                'image_url': n.image_url,
                'tag_name': n.tag.name,
                'author': n.author.username,
                'update_time': n.update_time.strftime('%Y年%m月%d日 %H:%M'),
            })

        # 创建返回给前端的数据
        data = {
            'total_pages': paginator.num_pages,  # num_pages属性是总的页数
            'news': news_info_list
        }

        return to_json_data(data=data)


class NewsDetailView(View):
    """
    Create news detail view
    load comments list
    url定义：'news/<int:news_id>'
    """

    def get(self, request, news_id):
        news = models.News.objects.select_related('tag', 'author'). \
            only('title', 'content', 'update_time', 'tag__name', 'author__username'). \
            filter(is_delete=False, id=news_id).first()

        if news:
            comments = models.Comments.objects.select_related('author', 'parent'). \
                only('content', 'author__username', 'update_time',
                     'parent__author__username', 'parent__content', 'parent__update_time'). \
                filter(is_delete=False, news_id=news_id)

            # 序列化输出
            comments_list = []
            for comm in comments:
                comments_list.append(comm.to_dict_data())

            return render(request, 'news/news_detail.html', locals())
        else:
            # raise Http404("<新闻{}>不存在😢".format(news_id))
            return HttpResponseNotFound('<h1>Page not found</h1>')


@method_decorator(cache_page(timeout=120, cache='page_cache'), name='dispatch')
class NewsBanner(View):
    """
    Creat news banner view

    请求方法：GET
    url定义： /news/banners/

    """

    def get(self, request):
        banners = models.Banner.objects.select_related('news').only('image_url', 'news__id', 'news__title'). \
                      filter(is_delete=False)[:constants.SHOW_BANNER_COUNT]

        # 序列化输出
        banners_info_list = []
        for b in banners:
            banners_info_list.append({
                'image_url': b.image_url,
                'news_id': b.news.id,
                'news_title': b.news.title,
            })

        # 创建返回给前端的数据
        data = {
            'banners': banners_info_list
        }

        return to_json_data(data=data)


class NewsCommentView(View):
    """
    Add new comment info
    url定义：/news/<int:news_id>/comments/
    """

    def post(self, request, news_id):
        if not request.user.is_authenticated:  # 认证用户是否登录
            return to_json_data(errno=Code.SESSIONERR, errmsg=error_map[Code.SESSIONERR])

        if not models.News.objects.only('id').filter(is_delete=False, id=news_id).exists():
            return to_json_data(errno=Code.PARAMERR, errmsg='新闻不存在！')

        # 从前端获取参数
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
        # 将json转化为dict
        dict_data = json.loads(json_data.decode('utf-8'))

        content = dict_data.get('content')
        if not content:
            return to_json_data(errno=Code.PARAMERR, errmsg='评论内容不能为空！')

        parent_id = dict_data.get('parent_id')
        try:
            if parent_id:
                parent_id = int(parent_id)
                if not models.Comments.objects.only('id'). \
                        filter(is_delete=False, id=parent_id, news_id=news_id).exists():
                    return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
        except Exception as e:
            logger.info("前端传过来的parent_id异常：\n{}".format(e))

        # 保存到数据库
        new_content = models.Comments()
        new_content.content = content
        new_content.news_id = news_id
        new_content.author = request.user
        new_content.parent_id = parent_id if parent_id else None
        new_content.save()

        return to_json_data(data=new_content.to_dict_data())


from haystack.views import SearchView as _SearchView


class SearchView(_SearchView):
    # 模版文件
    template = 'news/search.html'

    # 重写响应方式，如果请求参数q为空，返回模型News的热门新闻数据，否则根据参数q搜索相关数据
    def create_response(self):
        kw = self.request.GET.get('q', '')
        if not kw:
            show_all = True
            hot_news = models.HotNews.objects.select_related('news'). \
                only('news__title', 'news__image_url', 'news__id'). \
                filter(is_delete=False).order_by('priority', '-news__clicks')

            paginator = Paginator(hot_news, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
            try:
                page = paginator.page(int(self.request.GET.get('page', 1)))
            except PageNotAnInteger:
                # 如果参数page的数据类型不是整型，则返回第一页数据
                page = paginator.page(1)
            except EmptyPage:
                # 用户访问的页数大于实际页数，则返回最后一页的数据
                page = paginator.page(paginator.num_pages)

            return render(self.request, self.template, locals())

        else:
            show_all = False
            qs = super(SearchView, self).create_response()
            return qs
